package com.xshang.executor;

import com.xshang.config.BoundSql;
import com.xshang.pojo.Configuration;
import com.xshang.pojo.MappedStatement;
import com.xshang.util.placeholderParser.GenericTokenParser;
import com.xshang.util.placeholderParser.ParameterMapping;
import com.xshang.util.placeholderParser.ParameterMappingTokenHandler;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class SimpleExecutor implements Executor {

    private Connection connection = null;
    private PreparedStatement preparedStatement = null;
    private ResultSet resultSet = null;


    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object param) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, IntrospectionException, InstantiationException, InvocationTargetException {

        // 加载驱动，获取数据库连接
        connection = configuration.getDataSource().getConnection();

        // 获取preparedStatement预编译对象
        // 获取要执行的sql语句
        String sql = mappedStatement.getSql();
        // sql中 #{} 替换
        BoundSql boundSql = getBoundSql(sql);
        String finalSql = boundSql.getFinalSql();
        preparedStatement = connection.prepareStatement(finalSql);

        // 获取参数 类的全路径  com.xshang.pojo.User
        String parameterType = mappedStatement.getParameterType();
        Class<?> parameterTypeClass = null;
        if (parameterType != null) {
            parameterTypeClass = Class.forName(parameterType);
        }

        // 参数设置
        List<ParameterMapping> parameterMappings = boundSql.getList();
        // 根据parameterMappings中存储的值 通过反射 在参数对象中获取对应属性的值
        for (int i = 0; i < parameterMappings.size(); i++) {
            // id -- username  对于现在的sql
            String paramName = parameterMappings.get(i).getContent();
            // 反射  获取对应属性名的属性对象
            Field declaredField = parameterTypeClass.getDeclaredField(paramName);
            // 字段私有  暴力访问
            declaredField.setAccessible(true);
            Object value = declaredField.get(param);

            preparedStatement.setObject(i + 1, value);
        }
        // 执行sql 发起查询
        resultSet = preparedStatement.executeQuery();
        // 处理结果集

        ArrayList<E> resultList = new ArrayList<>();

        while (resultSet.next()) {
            // 元数据信息 包含了 字段名  字段的值
            ResultSetMetaData metaData = resultSet.getMetaData();
            // 结果数量
            int columnCount = metaData.getColumnCount();
            // 获取要封装的对象 com.xshang.pojo.User
            String resultType = mappedStatement.getResultType();
            Class<?> resultTypeClass = Class.forName(resultType);
            Object instance = resultTypeClass.newInstance();
            for (int i = 1; i <= columnCount; i++) {
                // 字段名
                String columnName = metaData.getColumnName(i);
                // 字段值
                Object value = resultSet.getObject(columnName);

                // 要封装到哪个对象
                // 属性描述器  通过api方法获取某个属性的读写方法
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                // 获取读写方法
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(instance, value);
            }
            resultList.add((E) instance);
        }
        // 返回结果
        return resultList;
    }

    private BoundSql getBoundSql(String sql) {
        // 标记处理器，配合标记解析器完成标记的处理工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        // 创建标记解析器
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        // 最终#{} 替换为 ? 的sql
        String finalSql = genericTokenParser.parse(sql);
        // 获取 #{name} 中值(name)的集合
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();

        return new BoundSql(finalSql, parameterMappings);
    }

    /**
     * 资源释放
     */
    @Override
    public void close() {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }

        if (preparedStatement != null) {
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }

        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }

    }
}
